package com.ywu.dev.admin.sdk.schedule;

import com.ywu.common.gson.GsonUtils;
import com.ywu.dev.admin.sdk.util.StorageConverter;
import com.ywu.dev.admin.spi.entity.*;
import com.ywu.dev.admin.spi.spi.YwuDevCommonSpi;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName ReportServiceInfoSchedule
 * @Description 上报数据
 * @Author GroundDemo
 * @Date 2025/3/25 8:54
 * @Version 1.0
 **/
@Component
@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery", value = "ip")
@Slf4j
public class ReportServiceInfoSchedule {

    @Autowired
    private MeterRegistry registry;

    @Autowired
    private HealthEndpoint healthEndpoint;

    @Value("${spring.cloud.nacos.discovery.ip}")
    private String ip;

    @Value("${server.port}")
    private String port;

    @Value("${ywu.dev.admin.enable:false}")
    private Boolean devAdminEnable;

    @Autowired
    private YwuDevCommonSpi ywuDevCommonSpi;

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").withZone(ZoneId.of("Asia/Shanghai"));

    public static final String PROCESS_UP_TIME = "process.uptime";

    public static final String PROCESS_CPU_USAGE = "process.cpu.usage";

    public static final String SYSTEM_CPU_USAGE = "system.cpu.usage";

    public static final String SYSTEM_CPU_COUNT = "system.cpu.count";

    public static final String JVM_GC = "jvm.gc.pause";

    public static final String THREAD_LIVE = "jvm.threads.live";

    public static final String THREAD_DAEMON = "jvm.threads.daemon";

    public static final String THREAD_PEAK = "jvm.threads.peak";

    public static final String MEMORY_USED = "jvm.memory.used";

    public static final String MEMORY_MAX = "jvm.memory.max";

    public static final String MEMORY_COMMIT = "jvm.memory.committed";

    /**
    * @Author GroundDemo
    * @Description 定时上报服务信息
    * @Date 8:58 2025/3/25
    * @Param []
    * @return void
    **/
    @Scheduled(cron = "${ywu.dev.admin.sdk.report.scheduleCron:0/10 * * * * ?}")
    public void doReportSchedule() {
        // 开关没开，不进行任何操作
        if (!devAdminEnable) {
            return;
        }
        if (StringUtils.isEmpty(ip) || StringUtils.isEmpty(port)) {
            return;
        }
        // 构建GC信息
        YwuDevOpsGcInfoEntity ywuDevOpsGcInfoEntity = buildGcInfo();
        // 构建服务信息 - 启动时间、CPU等信息
        YwuDevOpsProcessInfoEntity ywuDevOpsProcessInfoEntity = buildProcessInfo();
        // 构建CPU信息
        List<YwuDevOpsCpuInfoEntity> ywuDevOpsCpuInfoEntity = buildCpuInfo(ywuDevOpsProcessInfoEntity);
        // 构建线程信息
        List<YwuDevOpsThreadInfoEntity> ywuDevOpsThreadInfoEntity = buildThreadInfo();
        // 构建内存信息
        List<YwuDevOpsMemoryInfoEntity> ywuDevOpsMemoryInfoEntity = buildMemoryInfo();
        // 构建heal信息
        SystemHealth health = (SystemHealth) healthEndpoint.health();
        // 用于解析是下线还是异常 - 如果没有UP的服务，直接下线
        Map<String, Boolean> isWarnMap = new HashMap<>();
        List<YwuServiceDetailInfoEntity> healthInfo = buildHealth(health, "instance", isWarnMap);
        if (!CollectionUtils.isEmpty(isWarnMap)) {
            Boolean down = isWarnMap.getOrDefault("DOWN", false);
            // 如果存在下线的服务，看看有没有上线的服务，如果有的话，表示有异常服务下线，但是其他服务正常，将当前服务标记为WARN
            if (down) {
                Boolean up = isWarnMap.getOrDefault("UP", false);
                if (up) {
                    healthInfo.get(0).setStatus("WARN");
                }
            }
        }
        // 汇总
        YwuDevOpsGlobalEntity global = new YwuDevOpsGlobalEntity();
        global.setGcInfo(ywuDevOpsGcInfoEntity);
        global.setProcessInfo(ywuDevOpsProcessInfoEntity);
        global.setCpuInfo(ywuDevOpsCpuInfoEntity);
        global.setThreadInfo(ywuDevOpsThreadInfoEntity);
        global.setMemoryInfo(ywuDevOpsMemoryInfoEntity);
        global.setHealthInfo(healthInfo);
        log.debug("dev admin doReportSchedule info");
        // 调用接口提交数据
        ywuDevCommonSpi.serviceReportInfo(global);
    }


    /**
    * @Author GroundDemo
    * @Description 构建GC信息
    * @Date 10:06 2025/3/25
    * @Param []
    * @return com.ywu.dev.admin.sdk.entity.YwuDevOpsGcInfoEntity
    **/
    private YwuDevOpsGcInfoEntity buildGcInfo() {
        YwuDevOpsGcInfoEntity ywuDevOpsGcInfoEntity = new YwuDevOpsGcInfoEntity();
        Timer timer = registry.get(JVM_GC).timer();
        ywuDevOpsGcInfoEntity.setTotalGcCount(String.valueOf(timer.count()));
        ywuDevOpsGcInfoEntity.setMaxGcTime(String.valueOf(timer.max(TimeUnit.SECONDS)));
        ywuDevOpsGcInfoEntity.setTotalGcTime(String.valueOf(timer.totalTime(TimeUnit.SECONDS)));
        ywuDevOpsGcInfoEntity.setIpPort(ip + ":" + port);
        return ywuDevOpsGcInfoEntity;
    }

    /**
    * @Author GroundDemo
    * @Description 构建服务信息 - 启动时间、CPU等信息
    * @Date 10:37 2025/3/25
    * @Param []
    * @return com.ywu.dev.admin.sdk.entity.YwuDevOpsProcessInfoEntity
    **/
    private YwuDevOpsProcessInfoEntity buildProcessInfo() {
        YwuDevOpsProcessInfoEntity ywuDevOpsProcessInfoEntity = new YwuDevOpsProcessInfoEntity();
        // 构建启动时间
        Gauge upTimeGauge = registry.get(PROCESS_UP_TIME).gauge();
        // 毫秒转日期
        double value = upTimeGauge.value();
        String timerStr = getTimerStr(value);
        ywuDevOpsProcessInfoEntity.setProcessUpTime(timerStr);
        // 构建进程CPU使用率
        Gauge processCpuUsageGauge = registry.get(PROCESS_CPU_USAGE).gauge();
        if (processCpuUsageGauge.value() < 0) {
            ywuDevOpsProcessInfoEntity.setProcessCpuUsage("0.00000");
        } else {
            ywuDevOpsProcessInfoEntity.setProcessCpuUsage(String.format("%.5f", processCpuUsageGauge.value()));
        }
        // 构建系统CPU使用率
        Gauge systemCpuUsageGauge = registry.get(SYSTEM_CPU_USAGE).gauge();
        if (systemCpuUsageGauge.value() < 0) {
            ywuDevOpsProcessInfoEntity.setSystemCpuUsage("0.00000");
        } else {
            ywuDevOpsProcessInfoEntity.setSystemCpuUsage(String.format("%.5f", systemCpuUsageGauge.value()));
        }
        // 构建系统CPU核数
        Gauge systemCpuCountGauge = registry.get(SYSTEM_CPU_COUNT).gauge();
        ywuDevOpsProcessInfoEntity.setSystemCpuCount(String.valueOf((int) systemCpuCountGauge.value()));
        ywuDevOpsProcessInfoEntity.setIpPort(ip + ":" + port);
        return ywuDevOpsProcessInfoEntity;
    }

    /**
    * @Author GroundDemo
    * @Description 构建当前CPU使用率信息 - 基于采样点
    * @Date 10:54 2025/3/25
    * @Param [ywuDevOpsProcessInfoEntity]
    * @return java.util.List<com.ywu.dev.admin.sdk.entity.YwuDevOpsCpuInfoEntity>
    **/
    private List<YwuDevOpsCpuInfoEntity> buildCpuInfo(YwuDevOpsProcessInfoEntity ywuDevOpsProcessInfoEntity) {
        List<YwuDevOpsCpuInfoEntity> res = new ArrayList<>();
        // 设置采样时间
        LocalDateTime currentDate = LocalDateTime.now();
        String ipPort = ip + ":" + port;
        // 之前构建的参数可以直接使用
        YwuDevOpsCpuInfoEntity processInfo = buildCpuEntity(ywuDevOpsProcessInfoEntity.getProcessCpuUsage(), currentDate,
                YwuDevOpsCpuInfoEntity.PROCESS_TYPE, ipPort);
        res.add(processInfo);

        YwuDevOpsCpuInfoEntity systemInfo = buildCpuEntity(ywuDevOpsProcessInfoEntity.getSystemCpuUsage(), currentDate,
                YwuDevOpsCpuInfoEntity.SYSTEM_TYPE, ipPort);
        res.add(systemInfo);
        return res;
    }

    /**
    * @Author GroundDemo
    * @Description 构建当前线程信息 - 基于采样点
    * @Date 10:55 2025/3/25
    * @Param []
    * @return java.util.List<com.ywu.dev.admin.sdk.entity.YwuDevOpsThreadInfoEntity>
    **/
    private List<YwuDevOpsThreadInfoEntity> buildThreadInfo() {
        List<YwuDevOpsThreadInfoEntity> res = new ArrayList<>();
        LocalDateTime currentDate = LocalDateTime.now();
        String ipPort = ip + ":" + port;
        // 活跃线程
        Gauge threadLive = registry.get(THREAD_LIVE).gauge();
        YwuDevOpsThreadInfoEntity liveEntity = buildThreadEntity(String.valueOf((int) threadLive.value()), currentDate,
                YwuDevOpsThreadInfoEntity.THREAD_LIVE_TYPE, ipPort);
        res.add(liveEntity);
        // 守护线程
        Gauge threadDaemon = registry.get(THREAD_DAEMON).gauge();
        YwuDevOpsThreadInfoEntity daemonEntity = buildThreadEntity(String.valueOf((int) threadDaemon.value()), currentDate,
                YwuDevOpsThreadInfoEntity.THREAD_DAEMON_TYPE, ipPort);
        res.add(daemonEntity);
        // 线程峰值
        Gauge threadPeak = registry.get(THREAD_PEAK).gauge();
        YwuDevOpsThreadInfoEntity peakEntity =buildThreadEntity(String.valueOf((int) threadPeak.value()), currentDate,
                YwuDevOpsThreadInfoEntity.THREAD_PEAK_TYPE, ipPort);
        res.add(peakEntity);
        return res;
    }

    /**
    * @Author GroundDemo
    * @Description 构建当前内存使用情况信息 - 基于采样点
    * @Date 10:55 2025/3/25
    * @Param []
    * @return java.util.List<com.ywu.dev.admin.sdk.entity.YwuDevOpsMemoryInfoEntity>
    **/
    private List<YwuDevOpsMemoryInfoEntity> buildMemoryInfo() {
        List<YwuDevOpsMemoryInfoEntity> res = new ArrayList<>();
        LocalDateTime currentDate = LocalDateTime.now();
        String ipPort = ip + ":" + port;
        // 堆内存已使用情况
        Gauge usedAreaHeap = registry.get(MEMORY_COMMIT).tag("area", "heap").gauge();
        String usedAreaHeapMemoryUnit = getMemoryUnit(usedAreaHeap.value());
        YwuDevOpsMemoryInfoEntity usedAreaHeapEntity = buildMemoryEntity(usedAreaHeapMemoryUnit, currentDate,
                YwuDevOpsMemoryInfoEntity.MEMORY_HEAP_USED_TYPE, ipPort);
        res.add(usedAreaHeapEntity);
        // 非堆内存已使用情况
        Gauge usedAreaNonHeap = registry.get(MEMORY_COMMIT).tag("area", "nonheap").gauge();
        String usedAreaNonHeapMemoryUnit = getMemoryUnit(usedAreaNonHeap.value());
        YwuDevOpsMemoryInfoEntity usedAreaNonHeapEntity = buildMemoryEntity(usedAreaNonHeapMemoryUnit, currentDate,
                YwuDevOpsMemoryInfoEntity.MEMORY_NON_HEAP_USED_TYPE, ipPort);
        res.add(usedAreaNonHeapEntity);
        // 最大堆内存
        Gauge maxAreaHeap = registry.get(MEMORY_MAX).tag("area", "heap").gauge();
        String maxAreaHeapMemoryUnit = getMemoryUnit(maxAreaHeap.value());
        YwuDevOpsMemoryInfoEntity maxAreaHeapEntity = buildMemoryEntity(maxAreaHeapMemoryUnit, currentDate,
                YwuDevOpsMemoryInfoEntity.MEMORY_HEAP_MAX_TYPE, ipPort);
        res.add(maxAreaHeapEntity);
        // 最大非堆内存
        Gauge maxAreaNonHeap = registry.get(MEMORY_MAX).tag("area", "nonheap").gauge();
        String maxAreaNonHeapMemoryUnit = getMemoryUnit(maxAreaNonHeap.value());
        YwuDevOpsMemoryInfoEntity maxAreaNonHeapEntity = buildMemoryEntity(maxAreaNonHeapMemoryUnit, currentDate,
                YwuDevOpsMemoryInfoEntity.MEMORY_NON_HEAP_MAX_TYPE, ipPort);
        res.add(maxAreaNonHeapEntity);
        // 可用堆内存
        Gauge commitAreaHeap = registry.get(MEMORY_USED).tag("area", "heap").gauge();
        String commitAreaHeapMemoryUnit = getMemoryUnit(commitAreaHeap.value());
        YwuDevOpsMemoryInfoEntity commitAreaHeapEntity = buildMemoryEntity(commitAreaHeapMemoryUnit, currentDate,
                YwuDevOpsMemoryInfoEntity.MEMORY_HEAP_COMMIT_TYPE, ipPort);
        res.add(commitAreaHeapEntity);
        // 可用非堆内存
        Gauge commitAreaNonHeap = registry.get(MEMORY_USED).tag("area", "nonheap").gauge();
        String commitAreaNonHeapMemoryUnit = getMemoryUnit(commitAreaNonHeap.value());
        YwuDevOpsMemoryInfoEntity commitAreaNonHeapEntity = buildMemoryEntity(commitAreaNonHeapMemoryUnit, currentDate,
                YwuDevOpsMemoryInfoEntity.MEMORY_NON_HEAP_COMMIT_TYPE, ipPort);
        res.add(commitAreaNonHeapEntity);
        return res;
    }

    /**
    * @Author GroundDemo
    * @Description 将毫秒转换为日期字符串
    * @Date 10:36 2025/3/25
    * @Param [time]
    * @return java.lang.String
    **/
    public static String getTimerStr(double time) {
        int intInfo = (int) time;
        int day = intInfo / (3600 * 24);
        int hour = intInfo / 3600;
        int minutes = (intInfo % 3600) / 60;
        int sec = intInfo % 60;
        return String.format("%02d天%02d时%02d分%02d秒", day, hour, minutes, sec);
    }

    /**
    * @Author GroundDemo
    * @Description 获取内存大小 - 单位 MB
    * @Date 11:18 2025/3/25
    * @Param [num]
    * @return java.lang.String
    **/
    private static String getMemoryUnit(double num) {
        String valueStr = String.format("%f", num);
        valueStr = valueStr.substring(0, valueStr.indexOf("."));
        BigDecimal bytes = new BigDecimal(valueStr);
        BigDecimal resValue = bytes.divide(new BigDecimal("1048576"), RoundingMode.FLOOR);
        return resValue.toString();
    }

    /**
    * @Author GroundDemo
    * @Description 构建内存对象
    * @Date 11:23 2025/3/25
    * @Param [value, currentDate, type, ipPort]
    * @return com.ywu.dev.admin.sdk.entity.YwuDevOpsMemoryInfoEntity
    **/
    private YwuDevOpsMemoryInfoEntity buildMemoryEntity(String value, LocalDateTime currentDate, String type, String ipPort) {
        YwuDevOpsMemoryInfoEntity entity = new YwuDevOpsMemoryInfoEntity();
        String format = currentDate.atZone(ZoneId.systemDefault()).format(formatter);
        entity.setSamplingTime(format);
        entity.setType(type);
        entity.setIpPort(ipPort);
        entity.setSamplingValue(value);
        return entity;
    }

    /**
    * @Author GroundDemo
    * @Description 构建线程对象
    * @Date 11:24 2025/3/25
    * @Param [value, currentDate, type, ipPort]
    * @return com.ywu.dev.admin.sdk.entity.YwuDevOpsThreadInfoEntity
    **/
    private YwuDevOpsThreadInfoEntity buildThreadEntity(String value, LocalDateTime currentDate, String type, String ipPort) {
        YwuDevOpsThreadInfoEntity entity = new YwuDevOpsThreadInfoEntity();
        String format = currentDate.atZone(ZoneId.systemDefault()).format(formatter);
        entity.setSamplingTime(format);
        entity.setType(type);
        entity.setIpPort(ipPort);
        entity.setSamplingValue(value);
        return entity;
    }

    /**
    * @Author GroundDemo
    * @Description 构建CPU对象
    * @Date 11:24 2025/3/25
    * @Param [value, currentDate, type, ipPort]
    * @return com.ywu.dev.admin.sdk.entity.YwuDevOpsCpuInfoEntity
    **/
    private YwuDevOpsCpuInfoEntity buildCpuEntity(String value, LocalDateTime currentDate, String type, String ipPort) {
        YwuDevOpsCpuInfoEntity entity = new YwuDevOpsCpuInfoEntity();
        String format = currentDate.atZone(ZoneId.systemDefault()).format(formatter);
        entity.setSamplingTime(format);
        entity.setType(type);
        entity.setIpPort(ipPort);
        entity.setSamplingValue(value);
        return entity;
    }

    /**
    * @Author GroundDemo
    * @Description 构建health对象
    * @Date 16:31 2025/3/25
    * @Param [health, name, isWarnMap]
    * @return java.util.List<com.ywu.dev.admin.spi.entity.YwuServiceDetailInfoEntity>
    **/
    private List<YwuServiceDetailInfoEntity> buildHealth(HealthComponent health, String name, Map<String, Boolean> isWarnMap) {
        List<YwuServiceDetailInfoEntity> res = new ArrayList<>();
        YwuServiceDetailInfoEntity ywuServiceDetailInfoDto = new YwuServiceDetailInfoEntity();
        if (health instanceof CompositeHealth) {
            CompositeHealth tempHealth = (CompositeHealth) health;
            // 判断当前的components是否有值
            Map<String, HealthComponent> components = tempHealth.getComponents();
            Set<String> keys = components.keySet();
            for (String key : keys) {
                HealthComponent healthComponent = components.get(key);
                List<YwuServiceDetailInfoEntity> innerRes = buildHealth(healthComponent, key, isWarnMap);
                ywuServiceDetailInfoDto.addChildren(innerRes);
            }
        } else if (health instanceof Health) {
            Health tempHealth = (Health) health;
            // 解析details
            Map<String, Object> details = tempHealth.getDetails();
            if (!CollectionUtils.isEmpty(details)) {
                List<YwuServiceDetailInfoEntity> childrenList = new ArrayList<>();
                Set<String> keys = details.keySet();
                for (String key : keys) {
                    Object obj = details.get(key);
                    YwuServiceDetailInfoEntity temp = new YwuServiceDetailInfoEntity();
                    temp.setName(key);
                    if (obj instanceof List) {
                        List<Object> innerList = (List) obj;
                        List<YwuServiceDetailInfoEntity> innerChildrenList = new ArrayList<>();
                        for (Object o : innerList) {
                            YwuServiceDetailInfoEntity innerTemp = new YwuServiceDetailInfoEntity();
                            innerTemp.setValue(GsonUtils.serialize(o));
                            innerChildrenList.add(innerTemp);
                        }
                        if (!CollectionUtils.isEmpty(innerChildrenList)) {
                            temp.setChildren(innerChildrenList);
                        }
                    } else {
                        if (name.equals("diskSpace") && obj instanceof Long) {
                            temp.setValue(StorageConverter.convertToHighestDenominator(obj.toString()));
                        } else {
                            temp.setValue(obj.toString());
                        }
                    }
                    childrenList.add(temp);
                }
                ywuServiceDetailInfoDto.addChildren(childrenList);
            }
        }
        if (!name.equals("instance")) {
            if (health.getStatus().getCode().equals("UP")) {
                isWarnMap.put("UP", true);
            } else {
                isWarnMap.put("DOWN", true);
            }
        }
        ywuServiceDetailInfoDto.setName(name);
        ywuServiceDetailInfoDto.setStatus(health.getStatus().getCode());
        res.add(ywuServiceDetailInfoDto);
        return res;
    }

}
